package org.td.engine;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.FPSLogger;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Matrix4;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import org.td.engine.entity.*;
import org.td.engine.entity.tower.Tower;
import org.td.engine.entity.tower.TowerClass;
import org.td.engine.entity.tower.TurretClass;
import org.td.engine.map.GameMap;
import org.td.engine.map.MapCamera;
import org.td.engine.ui.*;

import java.util.Random;

public class GameScene implements Screen, InputProcessor {

    private InputMultiplexer inputMultiplexer;
    private SpriteBatch spriteBatch = new SpriteBatch();
    private ParticleFactory particleFactory;

    private EntityEnvironment entityEnvironment;

    private MonsterWave currentWave;
    private float waveDelay;
    private Player player;
    private SceneStage stage;

    private GameMap gameMap;
    private MapCamera mapCamera;

    private Entity selection;
    private InputHook inputHook;
    private TowerPopup towerPopup;

    private MapGroup mapActor;

    FPSLogger logger = new FPSLogger();
    public GameScene(Player player) {

        mapCamera = new MapCamera(32 * 32, 32 * 32, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        stage = new SceneStage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true, mapCamera);

        inputHook = new InputHook(this);
        inputMultiplexer = new InputMultiplexer(inputHook);
        inputMultiplexer.addProcessor(0, stage);
        Gdx.input.setInputProcessor(inputMultiplexer);

        this.player = player;

        gameMap = new GameMap(32, 32, mapCamera);
        entityEnvironment = new EntityEnvironment(gameMap);

        Random rnd = new Random();
        int monsterCount = rnd.nextInt(40) + 35;
        int[] monsters = new int[monsterCount];
        for (int i = 0; i < monsterCount; i++) {
            monsters[i] = rnd.nextInt(4);
        }

        startNewWave(new MonsterWave(new WaveData(), monsters));

        mapActor = new MapGroup(mapCamera);
        mapActor.setWidth(Gdx.graphics.getWidth());
        mapActor.setHeight(Gdx.graphics.getHeight());

        stage.addActor(mapActor);
        TowerList tl = new TowerList(new TurretIcon(), new TurretIcon(), new TurretIcon(), new TurretIcon(), new TurretIcon(), new TurretIcon());

        //tl.setCullingArea(new Rectangle(tl.getX(), tl.getY(), tl.getWidth(), tl.getHeight()));
        stage.addActor(tl);

        towerPopup = new TowerPopup(mapActor);
        TurretIcon ti1 = new TurretIcon();
        ti1.addListener(new ClickListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                towerPopup.collapse();
                return true;
            }
        });
        TurretIcon ti2 = new TurretIcon();
        ti2.addListener(new ClickListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                towerPopup.collapse();
                return true;
            }
        });
        TurretIcon ti3 = new TurretIcon();
        ti3.addListener(new ClickListener() {
            @Override
            public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
                towerPopup.getTower().markClear();
                towerPopup.collapse();
                return true;
            }
        });
        towerPopup.putIcon(ti1);
        towerPopup.putIcon(ti2);
        towerPopup.putIcon(ti3);
    }

    public void startNewWave(MonsterWave wave) {
        currentWave = wave;
        waveDelay = 0;
    }

    public void buildTower(int tileX, int tileY, TowerClass towerClass) {
        int width = towerClass.getWidth();
        int height = towerClass.getHeight();
        boolean acceptPathing = true;
        loop: for (int x = tileX; x <= tileX + width - 1; x++) {
            for (int y = tileY; y <= tileY + height - 1; y++) {
                if (gameMap.getPathing(x, y) > 0) {
                    acceptPathing = false;
                    break loop;
                }
            }
        }
        if (acceptPathing) {
            Tower newTower = new Tower(entityEnvironment, towerClass);
            newTower.setPosition(entityEnvironment.indexedToX(tileX), entityEnvironment.indexedToY(tileY));
            entityEnvironment.addEntity(newTower);
        }
    }
    
    private void render() {
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        spriteBatch.begin();
            spriteBatch.setTransformMatrix(new Matrix4());
            gameMap.render(spriteBatch);
            spriteBatch.flush();

            spriteBatch.setTransformMatrix(new Matrix4().setToTranslation(-mapCamera.getX(), -mapCamera.getY(), 0.0f));
            entityEnvironment.render(spriteBatch);
        spriteBatch.end();

        stage.act(Gdx.graphics.getDeltaTime());
        stage.draw();
    }

    private void update(float time) {
        if (currentWave != null && currentWave.getRemainingMonsters() > 0) {

            waveDelay += time;
            if (waveDelay >= currentWave.getDelay()) {
                Monster newMonster = new Monster(entityEnvironment, currentWave.getNextMonster(), gameMap.getPath(0)) ;
                entityEnvironment.addEntity(newMonster);
                waveDelay = 0;

            }
        } else {
            Random rnd = new Random();
            int monsterCount = rnd.nextInt(40) + 35;
            int[] monsters = new int[monsterCount];
            for (int i = 0; i < monsterCount; i++) {
                monsters[i] = rnd.nextInt(4);
            }
            startNewWave(new MonsterWave(new WaveData(), monsters));
        }

        entityEnvironment.update(time);

        if (towerPopup != null) {
            towerPopup.update(time);
        }
        logger.log();
    }

    @Override
    public void render(float time) {
        Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        update(time);
        render();
    }

    @Override
    public void resize(int i, int i1) {
        stage.setViewport(i, i1, true);
    }

    @Override
    public void show() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void hide() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void pause() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void resume() {
        //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public void dispose() {
        stage.dispose();
        entityEnvironment.dispose();
    }

    @Override
    public boolean keyDown(int keycode) {
        return false;
    }

    @Override
    public boolean keyUp(int keycode) {
        return false;
    }

    @Override
    public boolean keyTyped(char character) {
        return false;
    }

    @Override
    public boolean touchDown(int screenX, int screenY, int pointer, int button) {
        final int touchX = mapCamera.toWorldX(screenX);
        final int touchY = mapCamera.toWorldY(Gdx.graphics.getHeight() - screenY);

        Vector2 stageCoords = stage.toScreenCoordinates(new Vector2(touchX, touchY), mapCamera.projection);

        Entity e = entityEnvironment.pickEntity(new PickFilter() {
            @Override
            public boolean accept(Entity e) {
            if (e instanceof Monster) {
                return EntityEnvironment.distanceBetween(touchX, touchY, e) <= ((Monster)e).getSize();
            } else if (e instanceof Building) {
                return ((Building)e).containsPoint(touchX, touchY);
            }

                return false;
            }
        });

        if (e == null) {
            if (button == 0) {
                buildTower(mapCamera.toWorldX(screenX) / 32, (mapCamera.toWorldY(Gdx.graphics.getHeight() - screenY)) / 32, TowerClass.getRandom());
            } else {
                selection = null;
                if (towerPopup != null) {
                    towerPopup.collapse();
                }
            }
        } else {
            selection = e;

            if (selection instanceof Tower) {
                int slot = ((Tower)selection).getFreeTurretSlot();
                if (slot != -1) {
                    ParameterMap parameterMap = new ParameterMap();
                    parameterMap.putValue("textureFile", "turrets/turret1");
                    TurretClass turretClass = new TurretClass(parameterMap);
                    ((Tower)selection).insertTurret(slot, turretClass);
                }
                towerPopup.popup((Tower)selection);


            }
        }
        return false;
    }

    @Override
    public boolean touchUp(int screenX, int screenY, int pointer, int button) {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public boolean touchDragged(int screenX, int screenY, int pointer) {
        mapCamera.addPosition(-inputHook.getDragAmountX(), inputHook.getDragAmountY());
        return true;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public boolean mouseMoved(int screenX, int screenY) {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }

    @Override
    public boolean scrolled(int amount) {
        return false;  //To change body of implemented methods use File | Settings | File Templates.
    }
}
